package com.wechat.service.impl;

import com.wechat.entity.dto.MessageSendDto;
import com.wechat.entity.dto.SysSettingDto;
import com.wechat.entity.dto.UserContactSearchResultDto;
import com.wechat.entity.enums.*;
import com.wechat.entity.po.*;
import com.wechat.entity.query.*;
import com.wechat.entity.vo.PaginationResultVO;
import com.wechat.exception.BusinessException;
import com.wechat.mappers.*;
import com.wechat.redis.RedisComponent;
import com.wechat.service.UserContactService;
import com.wechat.utils.StringTools;
import com.wechat.websocket.ChannelContextUtils;
import com.wechat.websocket.MessageHandler;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 联系人 业务接口实现
 */
@Service("userContactService")
public class UserContactServiceImpl implements UserContactService {

    @Resource
    private UserContactMapper<UserContact, UserContactQuery> userContactMapper;

    @Resource
    private GroupInfoMapper<GroupInfo, GroupInfoQuery> groupInfoMapper;

    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

    @Resource
    private RedisComponent redisComponent;

    @Resource
    private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;

    @Resource
    private ChatSessionUserMapper<ChatSessionUser, ChatSessionUserQuery> chatSessionUserMapper;

    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

    @Resource
    private MessageHandler messageHandler;

    @Resource
    private ChannelContextUtils channelContextUtils;


    /**
     * 根据条件查询列表
     */
    @Override
    public List<UserContact> findListByParam(UserContactQuery param) {
        return this.userContactMapper.selectList(param);
    }

    /**
     * 根据条件查询列表
     */
    @Override
    public Integer findCountByParam(UserContactQuery param) {
        return this.userContactMapper.selectCount(param);
    }

    /**
     * 分页查询方法
     */
    @Override
    public PaginationResultVO<UserContact> findListByPage(UserContactQuery param) {
        int count = this.findCountByParam(param);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

        SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
        param.setSimplePage(page);
        List<UserContact> list = this.findListByParam(param);
        PaginationResultVO<UserContact> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }

    /**
     * 新增
     */
    @Override
    public Integer add(UserContact bean) {
        return this.userContactMapper.insert(bean);
    }

    /**
     * 批量新增
     */
    @Override
    public Integer addBatch(List<UserContact> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.userContactMapper.insertBatch(listBean);
    }

    /**
     * 批量新增或者修改
     */
    @Override
    public Integer addOrUpdateBatch(List<UserContact> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.userContactMapper.insertOrUpdateBatch(listBean);
    }

    /**
     * 多条件更新
     */
    @Override
    public Integer updateByParam(UserContact bean, UserContactQuery param) {
        StringTools.checkParam(param);
        return this.userContactMapper.updateByParam(bean, param);
    }

    /**
     * 多条件删除
     */
    @Override
    public Integer deleteByParam(UserContactQuery param) {
        StringTools.checkParam(param);
        return this.userContactMapper.deleteByParam(param);
    }

    /**
     * 根据UserIdAndContactId获取对象
     */
    @Override
    public UserContact getUserContactByUserIdAndContactId(String userId, String contactId) {
        return this.userContactMapper.selectByUserIdAndContactId(userId, contactId);
    }

    /**
     * 根据UserIdAndContactId修改
     */
    @Override
    public Integer updateUserContactByUserIdAndContactId(UserContact bean, String userId, String contactId) {
        return this.userContactMapper.updateByUserIdAndContactId(bean, userId, contactId);
    }

    /**
     * 根据UserIdAndContactId删除
     */
    @Override
    public Integer deleteUserContactByUserIdAndContactId(String userId, String contactId) {
        return this.userContactMapper.deleteByUserIdAndContactId(userId, contactId);
    }

    /**
     * 搜索联系人
     */
    @Override
    public UserContactSearchResultDto searchContact(String userId, String contactId) {
        //先获取联系人类型：1.群组 2.联系人
        UserContactTypeEnum contactType = UserContactTypeEnum.getByPrefix(contactId);
        if (contactType == null) {
            return null;
        }
        UserContactSearchResultDto resultDto = new UserContactSearchResultDto();
        switch (contactType) {
            case USER:
                UserInfo userInfo = userInfoMapper.selectByUserId(contactId);
                if (userInfo == null) {
                    return null;
                }
                BeanUtils.copyProperties(userInfo, resultDto);
                break;
            case GROUP:
                GroupInfo groupInfo = groupInfoMapper.selectByGroupId(contactId);
                if (groupInfo == null) {
                    return null;
                }
                resultDto.setNickName(groupInfo.getGroupName());
                break;
        }

        resultDto.setContactType(contactType.toString());
        resultDto.setContactId(contactId);
        //如果查到是自己的id
        if (userId.equals(contactId)) {
            resultDto.setStatus(UserContactStatusEnum.FRIEND.getStatus());
            return resultDto;
        }
        //获取查询的联系人状态，是否是好友---用于后续添加
        UserContact userContact = userContactMapper.selectByUserIdAndContactId(userId, contactId);
        if (userContact == null) {
            resultDto.setStatus(UserContactStatusEnum.NOT_FRIEND.getStatus());
        } else {
            resultDto.setStatus(userContact.getStatus());
        }
        return resultDto;

    }

    /**
     * 添加联系人
     * 包含双向添加的逻辑
     */
    @Override
    public void addContact(String applyUserId, String receiveUserId, String contactId, Integer contactType, String applyInfo) {
        //群聊上限判断
        if(UserContactTypeEnum.GROUP.getType() == contactType){
            UserContactQuery param = UserContactQuery.builder()
                    .contactId(contactId)
                    .status(UserContactStatusEnum.FRIEND.getStatus())
                    .build();
            Integer count = userContactMapper.selectCount(param);
            SysSettingDto sysSettingDto = redisComponent.getSysSetting();
            if(count >= sysSettingDto.getMaxGroupMemberCount()){
                throw new BusinessException("群聊人数已满");
            }
        }
        Date curDate = new Date();
        //同意，双方互相添加好友
        List<UserContact> contactList = new ArrayList<>();
        //申请方A添加被申请方B为好友
        UserContact applyUserContact = UserContact.builder()
                .userId(applyUserId)
                .contactId(contactId)
                .contactType(contactType)
                .createTime(curDate)
                .status(UserContactStatusEnum.FRIEND.getStatus())
                .lastUpdateTime(curDate)
                .build();
        contactList.add(applyUserContact);
        //被申请方B添加申请方A为好友 或 群组不需要添加对方为好友
        //被申请方B添加申请方A为好友
        if(UserContactTypeEnum.USER.getType() == contactType){
            applyUserContact = UserContact.builder()
                    .userId(receiveUserId)
                    .contactId(applyUserId)
                    .contactType(contactType)
                    .createTime(curDate)
                    .status(UserContactStatusEnum.FRIEND.getStatus())
                    .lastUpdateTime(curDate)
                    .build();
            contactList.add(applyUserContact);
        }
        userContactMapper.insertOrUpdateBatch(contactList);//批量添加
        /*
        添加好友（用户A添加用户B）：
        两次 Redis 操作都执行，A的好友列表添加B，B的好友列表添加A
        加入群组（用户A加入群组G）：
        第一次操作跳过，只执行第二次操作（A的联系人列表添加群组G）
         */
        //如果是好友，接收人也添加申请人为好友 添加缓存
        if(UserContactTypeEnum.USER.getType().equals(contactType)){
            //当A申请B为好友，B同意后，需要在B的好友列表中添加A
            redisComponent.addUserContact(receiveUserId, applyUserId);
        }
        //为申请方添加对方为好友/或者添加群组
        //A申请B为好友，B同意后在A的好友列表中添加B
        redisComponent.addUserContact(applyUserId, contactId);

        //创建会话
        String sessionId = null;
        //设置会话Id
        if(UserContactTypeEnum.USER.getType().equals(contactType)){
            sessionId = StringTools.getChatSessionId4User(new String[]{applyUserId, contactId});
        }else{
            sessionId = StringTools.getChatSessionId4Group(contactId);
        }

        ArrayList<ChatSessionUser> chatSessionUserList = new ArrayList<>();//用于存储参与会话的用户
        if(UserContactTypeEnum.USER.getType().equals(contactType)){//添加好友
            //创建好友间的会话
            ChatSession chatSession = new ChatSession();//表示一个会话（存储会话的公共信息）
            chatSession.setSessionId(sessionId);
            chatSession.setLastMessage(applyInfo);
            chatSession.setLastReceiveTime(curDate.getTime());
            chatSessionMapper.insertOrUpdate(chatSession);

            //申请人方session
            ChatSessionUser applySessionUser = new ChatSessionUser();//表示一个会话用户（存储会话用户信息）---谁参与会话，用于显示会话列表
            applySessionUser.setSessionId(sessionId);
            applySessionUser.setUserId(applyUserId);
            applySessionUser.setContactId(contactId);
            UserInfo contactUserInfo = userInfoMapper.selectByUserId(contactId);//获取联系人信息
            applySessionUser.setContactName(contactUserInfo.getNickName());
            chatSessionUserList.add(applySessionUser);

            //被申请人方session
            ChatSessionUser contactSessionUser = new ChatSessionUser();//写两次是用于不同的会话用户展示自己的会话列表
            contactSessionUser.setSessionId(sessionId);
            contactSessionUser.setUserId(contactId);
            contactSessionUser.setContactId(applyUserId);
            UserInfo applyUserInfo = userInfoMapper.selectByUserId(applyUserId);//获取申请人信息
            contactSessionUser.setContactName(applyUserInfo.getNickName());
            chatSessionUserList.add(contactSessionUser);

            chatSessionUserMapper.insertOrUpdateBatch(chatSessionUserList);//批量添加会话用户

            //聊天消息表
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setSessionId(sessionId);
            chatMessage.setMessageType(MessageTypeEnum.ADD_FRIEND.getType());
            chatMessage.setMessageContent(applyInfo);
            chatMessage.setSendUserId(applyUserId);
            chatMessage.setContactId(contactId);
            chatMessage.setSendUserNickName(applyUserInfo.getNickName());
            chatMessage.setSendTime(curDate.getTime());
            chatMessage.setContactType(UserContactTypeEnum.USER.getType());
            chatMessageMapper.insert(chatMessage);

            MessageSendDto messageSendDto = new MessageSendDto<>();
            BeanUtils.copyProperties(chatMessage,messageSendDto);//将chatMessage属性复制给messageSendDto
            //发给双方服务器发送消息
            messageHandler.sendMessage(messageSendDto);//消息类型：ADD_FRIEND，接收方---双方
            //发给申请方发送消息（用于提示添加联系人成功）
            /*
            setMessageType(MessageTypeEnum.ADD_FRIEND_SELF.getType())
            这里只是一种间接的设置，主要用于发送消息功能中（自己给自己发送信息不需要提示），最终还是要将type改为添加好友
            为什么这样做？前端根据不同的消息类型做不同处理：
            // 1. ADD_FRIEND_SELF: 发信息给自己
            // 2. ADD_FRIEND: 正常显示好友添加信息
             */
            messageSendDto.setMessageType(MessageTypeEnum.ADD_FRIEND_SELF.getType());//临时类型
            messageSendDto.setContactId(applyUserId);//设置发送给申请方（因为消息要发给自己，所以联系人就是自己）
            messageSendDto.setExtendData(contactUserInfo);//携带被添加人信息，用于显示
            messageHandler.sendMessage(messageSendDto);//消息类型：ADD_FRIEND_SELF，接收方---仅申请人

        }else{//添加群组
            //创建群聊会话
            ChatSessionUser chatSessionUser = new ChatSessionUser();
            chatSessionUser.setSessionId(sessionId);
            chatSessionUser.setUserId(applyUserId);
            chatSessionUser.setContactId(contactId);
            GroupInfo groupInfo = groupInfoMapper.selectByGroupId(contactId);
            chatSessionUser.setContactName(groupInfo.getGroupName());
            chatSessionUserMapper.insertOrUpdate(chatSessionUser);

            UserInfo applyUserInfo = userInfoMapper.selectByUserId(applyUserId);
            String sendMessage = String.format(MessageTypeEnum.ADD_GROUP.getInitMessage(), applyUserInfo.getNickName());

            //添加session信息
            ChatSession chatSession = new ChatSession();
            chatSession.setSessionId(sessionId);
            chatSession.setLastMessage(sendMessage);
            chatSession.setLastReceiveTime(curDate.getTime());
            chatSessionMapper.insertOrUpdate(chatSession);//使用update是因为可能需要更新最后接受信息与时间

            //添加聊天消息
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setSessionId(sessionId);
            chatMessage.setMessageType(MessageTypeEnum.ADD_GROUP.getType());
            chatMessage.setMessageContent(sendMessage);
            chatMessage.setContactId(contactId);
            chatMessage.setContactType(UserContactTypeEnum.GROUP.getType());
            chatMessage.setSendTime(curDate.getTime());
            chatMessage.setStatus(MessageStatusEnum.SENDED.getStatus());
            chatMessageMapper.insert(chatMessage);

            //将群组添加到联系人中
            redisComponent.addUserContact(applyUserId, contactId);

            //将联系人通道添加到群组通道中
            channelContextUtils.addUser2Group(applyUserId, contactId);

            //发送消息dto
            MessageSendDto messageSendDto = new MessageSendDto<>();
            BeanUtils.copyProperties(chatMessage,messageSendDto);

            //获取群聊人数
            UserContactQuery query = new UserContactQuery();
            query.setContactId(contactId);
            query.setStatus(UserContactStatusEnum.FRIEND.getStatus());
            Integer count = userContactMapper.selectCount(query);

            messageSendDto.setMemberCount(count);
            messageSendDto.setContactName(groupInfo.getGroupName());

            //发送
            messageHandler.sendMessage(messageSendDto);

        }


    }

    /**
     * 移除联系人---修改联系人状态
     */
    @Override
    @Transactional
    public void removeUserContact(String userId, String contactId, UserContactStatusEnum statusEnum) {
        Date curDate = new Date();//创建时间戳---得到当前时间
        //用户移除好友---更新自己列表中对方的状态
        UserContact myUserContact= new UserContact();
        myUserContact.setStatus(statusEnum.getStatus());
        myUserContact.setLastUpdateTime(curDate);
        userContactMapper.updateByUserIdAndContactId(myUserContact, userId, contactId);

        //在对方列表中修改自己的状态
        UserContact friendUserContact = new UserContact();
        if(statusEnum == UserContactStatusEnum.DEL){
            friendUserContact.setStatus(UserContactStatusEnum.DEL_BE.getStatus());
        }else if(statusEnum == UserContactStatusEnum.BLACKLIST){
            friendUserContact.setStatus(UserContactStatusEnum.BLACKLIST_BE.getStatus());
        }
        friendUserContact.setLastUpdateTime(curDate);
        userContactMapper.updateByUserIdAndContactId(friendUserContact, contactId, userId);

        //从我的好友列表缓存中删除好友
        redisComponent.removeUserContact(userId, contactId);

        //从好友列表缓存中删除我
        redisComponent.removeUserContact(contactId, userId);


    }

    /**
     * 移除群组
     * @param userId
     * @param groupId
     * @param contactId
     * @param statusEnum
     */
    @Override
    public void removeGroupContact(String userId, String groupId, String contactId, UserContactStatusEnum statusEnum) {
        GroupInfo groupInfo = groupInfoMapper.selectByGroupId(groupId);
        if (null == groupInfo || !groupInfo.getGroupOwnerId().equals(userId)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        UserContact userContact = new UserContact();
        userContact.setStatus(statusEnum.getStatus());
        userContactMapper.updateByUserIdAndContactId(userContact, contactId, groupId);
        //将群组从群员列表中删除
        redisComponent.removeUserContact(contactId, groupId);

    }

    /**
     * 添加机器人联系人
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addContact4Robot(String userId) {
        Date curDate = new Date();
        SysSettingDto sysSettingDto = redisComponent.getSysSetting();
        String contactId = sysSettingDto.getRobotUid();
        String contactName = sysSettingDto.getRobotNickName();
        String msg = sysSettingDto.getRobotWelcome();
        msg = StringTools.cleanHtmlTag(msg);//去掉html标签
        //添加机器人好友
        UserContact userContact = UserContact.builder()
                .userId(userId)
                .contactId(contactId)
                .contactType(UserContactTypeEnum.USER.getType())
                .createTime(curDate)
                .lastUpdateTime(curDate)
                .status(UserContactStatusEnum.FRIEND.getStatus())
                .build();
        userContactMapper.insert(userContact);
        //添加会话信息
        String sessionId =  StringTools.getChatSessionId4User(new String[]{userId, contactId});
        ChatSession chatSession = new ChatSession();
        chatSession.setSessionId(sessionId);
        chatSession.setLastMessage(msg);
        chatSession.setLastReceiveTime(curDate.getTime());
        chatSessionMapper.insert(chatSession);
        //添加会话人信息
        ChatSessionUser chatSessionUser = new ChatSessionUser();
        chatSessionUser.setSessionId(sessionId);
        chatSessionUser.setUserId(userId);
        chatSessionUser.setContactId(contactId);
        chatSessionUser.setContactName(contactName);
        chatSessionUserMapper.insert(chatSessionUser);
        //添加聊天信息
        ChatMessage chatMessage = ChatMessage.builder()
                .sessionId(sessionId)
                .messageType(MessageTypeEnum.CHAT.getType())
                .messageContent(msg)
                .sendUserId(contactId)
                .sendUserNickName(contactName)
                .sendTime(curDate.getTime())
                .contactId(userId)
                .contactType(UserContactTypeEnum.USER.getType())
                .status(MessageStatusEnum.SENDED.getStatus())
                .build();
        chatMessageMapper.insert(chatMessage);
    }
}
